sudo chown -R $USER:$USER /usr/lib/iot-test
Ezdb@123456

app.commandLine.appendSwitch('ignore-certificate-errors');
app.commandLine.appendSwitch('allow-insecure-localhost');

npm install serialport

// 监听渲染进程的请求，返回串口列表
ipcMain.handle('get-serial-ports', async () => {
  try {
    // 获取所有可用的串口
    const ports = await SerialPort.list();
    // 提取端口信息，只返回需要的字段
    return ports.map(port => ({
      comName: port.path,       // 端口号，如 COM3 或 /dev/ttyUSB0
      manufacturer: port.manufacturer,  // 制造商
      vendorId: port.vendorId,  // 供应商ID
      productId: port.productId  // 产品ID
    }));
  } catch (error) {
    console.error('获取串口列表失败:', error);
    throw error;
  }
});


const { contextBridge, ipcRenderer } = require('electron');

// 向渲染进程暴露API
contextBridge.exposeInMainWorld('electronAPI', {
  getSerialPorts: () => ipcRenderer.invoke('get-serial-ports')
});



An unhandled rejection has occurred inside Forge:
Error: node-gyp failed to rebuild '/home/wubi/work/iot_test/node_modules/@serialport/bindings-cpp'
at ChildProcess.<anonymous> (/home/wubi/work/iot_test/node_modules/@electron/rebuild/lib/module-type/node-gyp/node-gyp.js:121:24)
    at ChildProcess.emit (node:events:517:28)
    at ChildProcess._handle.onexit (node:internal/child_process:292:12)

sudo apt-get update
sudo apt-get install -y build-essential python3 libudev-dev


npm install --save-dev electron-rebuild
./node_modules/.bin/electron-rebuild

[{"id":"f2b3c541197aea23","type":"tab","label":"网关设备-协议扩展性","disabled":false,"info":"","env":[]},{"id":"c6f6c91c824708d6","type":"subflow","name":"数据指标判断","info":"","category":"","in":[{"x":60,"y":80,"wires":[{"id":"be2e08cb417996ac"}]}],"out":[],"env":[],"meta":{},"color":"#DDAA99"},{"id":"410a1eb0ca263f28","type":"subflow","name":"外部日志输出","info":"","category":"","in":[{"x":60,"y":80,"wires":[{"id":"eb1bfab1197adcda"},{"id":"2653add6d4ba1bcf"}]}],"out":[],"env":[],"meta":{},"color":"#DDAA99"},{"id":"b676dbe90b296890","type":"subflow","name":"外部管理接口 ","info":"","category":"","in":[],"out":[{"x":900,"y":460,"wires":[{"id":"8c69042a7c33e65a","port":0}]}],"env":[],"meta":{},"color":"#DDAA99","outputLabels":["启动请求"]},{"id":"0b96e65cf9d2c6a0","type":"subflow","name":"测试策略","info":"","category":"","in":[],"out":[{"x":1020,"y":300,"wires":[{"id":"c0ad4a617d16fb28","port":0}]},{"x":840,"y":260,"wires":[{"id":"911b4a0523a9a5ca","port":0}]}],"env":[],"meta":{},"color":"#DDAA99","outputLabels":["采集开始","测试结束"]},{"id":"da7a0f42ae981076","type":"subflow","name":"被测设备","info":"","category":"","in":[{"x":60,"y":140,"wires":[]}],"out":[{"x":580,"y":140,"wires":[]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"020a746569c76785","type":"tls-config","z":"410a1eb0ca263f28","name":"","cert":"","key":"","ca":"","certname":"cert.pem","keyname":"privkey.pem","caname":"","servername":"","verifyservercert":false,"alpnprotocol":""},{"id":"bc9bfb8feab833a5","type":"tls-config","z":"b676dbe90b296890","name":"","cert":"","key":"","ca":"","certname":"privkey.pem","keyname":"cert.pem","caname":"","servername":"","verifyservercert":false,"alpnprotocol":""},{"id":"1da57e4df77a3827","type":"tls-config","z":"0b96e65cf9d2c6a0","name":"","cert":"","key":"","ca":"","certname":"cert.pem","keyname":"privkey.pem","caname":"","servername":"","verifyservercert":false,"alpnprotocol":""},{"id":"be2e08cb417996ac","type":"function","z":"c6f6c91c824708d6","name":"指标参数判断","func":"\nvar t \n try {\n t = msg.payload.toUpperCase()\n} catch (e) {\n  t = msg.payload\n}; //采集到的数据\nvar type_input = \"{{type_input}}\"//由服务端处理\nvar type = \"number\"//number string send_speed collect_speed list raw location\ntype = type_input;\nvar min_input = \"{{min_input}}\"//由服务端处理\nvar max_input = \"{{max_input}}\"//由服务端处理\nvar min = -20;\nvar max = 100;\nvar value_input = \"{{value_input}}\".toUpperCase();////由服务端处理\nvar listItem={\ntype,\nstate:null,\nvalue:null\n}\nif (!Number.isNaN(Number(min_input))) {\n  min = Number(min_input)\n}\nif (!Number.isNaN(Number(max_input))) {\n  max = Number(max_input)\n}\n\nif(type == \"number\" ){\n  if (t <= max && t >= min) {\n    listItem.state = 1;\n    listItem.value =Math.round(t*100)/100\n  } else {\n    listItem.state = 0;\n    listItem.value = Math.round(t * 100) / 100\n  }\n\n} else if (type == \"collect_speed\" ){\n  var current_time = new Date().getTime();\n  var collect_speed = 1000 / (current_time - global.get(\"timing\"))\n  collect_speed = parseFloat(Math.round(collect_speed * 100) / 100)//单位Hz\n  if (collect_speed >= min){\n    listItem.state = 1;\n    listItem.value = collect_speed\n  }else{\n    listItem.state = 0;\n    listItem.value = collect_speed\n  }\n\n  global.set(\"timing\", 0)\n}\nelse if(type == \"string\"){\n\n  if(t == value_input){\n    listItem.state = 1;\n    listItem.value = t\n  }else{\n    listItem.state = 0;\n    listItem.value = t\n  }\n}else if(type == \"send_speed\"){\n  if (msg.speed >= min) {\n    listItem.state = 1;\n    listItem.value = msg.speed\n  } else {\n    listItem.state = 0;\n    listItem.value = msg.speed\n  }\n}else if(type == \"list\"){//列表值对比\n  var list =\"{{list}}\".match(/'([^']*)'/g).map(match => match.slice(1, -1));//服务器替换list如[\"123\",\"456\"]\n    listItem.state = 0;\n    listItem.value = t\n    if(list.length >0){\n      for (let i = 0; i < list.length;  i++) {\n        let item=list[i]\n         try {\n item = item.toUpperCase()\n} catch (e) {\n\n}; \n        if (item == t){\n           listItem.state = 1;\n           listItem.value = t\n            break;\n         }\n      }\n    }\n}else if(type === \"raw\"){\n    if( msg.elapsedTime <= value_input){\n    listItem.state = 1;\n    listItem.value = msg.elapsedTime;\n  }else{\n    listItem.state = 0;\n    listItem.value = msg.elapsedTime;\n  }\n}else if(type === \"location\"){\n\nfunction haversine(lat1, lon1, lat2, lon2) {\n    const R = 6371e3; // 地球半径（米）\n    const φ1 = lat1 * Math.PI / 180;\n    const φ2 = lat2 * Math.PI / 180;\n    const Δφ = (lat2 - lat1) * Math.PI / 180;\n    const Δλ = (lon2 - lon1) * Math.PI / 180;\n \n    const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +\n              Math.cos(φ1) * Math.cos(φ2) *\n              Math.sin(Δλ/2) * Math.sin(Δλ/2);\n    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\n \n    return R * c; // 返回米，如需公里则除以1000\n}\n  const distance = haversine(t.latitude, t.longitude, value_input.split(\",\")[1], value_input.split(\",\")[0])\n  if (distance < min){\n    listItem.state = 1;\n    listItem.value = Math.round(distance * 100) / 100 + 'm'\n}else{\n    listItem.state = 0;\n    listItem.value = Math.round(distance * 100) / 100 + 'm'\n  \n}\n}\nvar allList = global.get(\"result\") || [];\nallList.push(listItem);\nglobal.set(\"result\", allList);\nglobal.set(\"has_result\", 1);\nmsg.payload = { \"msg\": `采集数据：${listItem.value}，指标检测：${listItem.state}`, \"code\": 0 };\nreturn msg;\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":220,"y":80,"wires":[["1277fc352696634b"]]},{"id":"1277fc352696634b","type":"subflow:410a1eb0ca263f28","z":"c6f6c91c824708d6","name":"","x":440,"y":80,"wires":[]},{"id":"15e9c48ab6cc5d0c","type":"http request","z":"410a1eb0ca263f28","name":"日志上报","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"020a746569c76785","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":500,"y":80,"wires":[["df26f8041ec2388b"]]},{"id":"eb1bfab1197adcda","type":"function","z":"410a1eb0ca263f28","name":"日志请求数据处理","func":"///admin/wlp/taskLog/add\nmsg.url = `${global.get(\"host\")}/admin/wlp/taskLog/add`\nmsg.headers = {\n    \"Authorization\":global.get(\"token\"),\n    \"Content-Type\": \"application/json\"\n}\nvar status = global.get(\"flow_status\");\nmsg.payload = {\n    data:{\n     \"testTaskId\": global.get(\"testTaskId\"),\n     \"testRecordId\":global.get(\"testRecordId\"),\n     \"msg\": msg.payload.msg\n}}\nnode.error(1);\nnode.error(global.get(\"testRecordId\"));\nnode.error(2);\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":290,"y":80,"wires":[["15e9c48ab6cc5d0c"]]},{"id":"2653add6d4ba1bcf","type":"debug","z":"410a1eb0ca263f28","name":"测试日志打印","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":320,"y":140,"wires":[]},{"id":"df26f8041ec2388b","type":"debug","z":"410a1eb0ca263f28","name":"日志上报响应","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":720,"y":80,"wires":[]},{"id":"e240fc5f94bf8f0e","type":"http in","z":"b676dbe90b296890","name":"HTTP接口","url":"/test","method":"get","upload":false,"swaggerDoc":"","x":80,"y":300,"wires":[["b392bde36a456c1b","8c69042a7c33e65a","b721ed0459e1f45e","042194dedfa417f2","09e9ca0caf4be738","f14cd04ff0981269","6c9a53e49712d426"]]},{"id":"b392bde36a456c1b","type":"debug","z":"b676dbe90b296890","name":"调试打印","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":440,"y":120,"wires":[]},{"id":"39280fec7a243b6d","type":"http response","z":"b676dbe90b296890","name":"响应","statusCode":"200","headers":{"content-type":"application/json"},"x":730,"y":340,"wires":[]},{"id":"8c69042a7c33e65a","type":"function","z":"b676dbe90b296890","name":"启动请求","func":"if (msg.payload.op == \"start\"){\n    var status = global.get(\"flow_status\");\n    if(status == \"start\"){\n        msg.payload = { \"msg\": \"启动失败,流程正在运行\", \"code\": -1, \"is_init\": false,\n        \"errorCode\": \"ERROR\",\n        \"errorMessage\": \"启动失败,流程正在运行\",\n        \"success\": false\n        };\n    } else if (status == \"pause\"){\n        global.set(\"flow_status\", \"start\");\n        msg.payload = { \"msg\": \"启动成功\", \"code\": 0, \"is_init\": false ,\n          \"errorCode\": \"NO-ERROR\",\n            \"errorMessage\": \"NO-MESSAGE\",\n            \"success\": true\n        };\n    }else{\n        global.set(\"flow_status\", \"start\");\n        msg.payload = { \"msg\": \"启动成功\", \"code\": 0, \"is_init\": true ,\n          \"errorCode\": \"NO-ERROR\",\n            \"errorMessage\": \"NO-MESSAGE\",\n            \"success\": true\n        };\n    }\n     global.set(\"timing\",new Date().getTime());\n    return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":360,"wires":[["39280fec7a243b6d","be9582addda9e660","bf932223988ed392","8d94373e38b4b93b"]]},{"id":"b721ed0459e1f45e","type":"function","z":"b676dbe90b296890","name":"停止请求","func":"if (msg.payload.op == \"stop\"){\n    global.set(\"flow_status\", \"stop\");\n    msg.payload = { \"status\": \"停止成功\", \"code\": 0 ,\n    \"errorCode\": \"NO-ERROR\",\n    \"errorMessage\": \"NO-MESSAGE\",\n    \"success\": true\n    }\n    return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":300,"wires":[["39280fec7a243b6d","be9582addda9e660","bf932223988ed392"]]},{"id":"042194dedfa417f2","type":"function","z":"b676dbe90b296890","name":"暂停请求","func":"if (msg.payload.op == \"pause\"){\n    if(global.get(\"flow_status\") == \"start\"){\n       global.set(\"flow_status\", \"pause\");\n       msg.payload = { \"msg\": \"暂停成功\", \"code\": 0,\n       \"errorCode\": \"NO-ERROR\",\n    \"errorMessage\": \"NO-MESSAGE\",\n    \"success\": true }\n    }else{\n        msg.payload = { \"msg\": \"暂停失败，流程未运行\", \"code\":-1,\n        \"errorCode\": \"ERROR\",\n        \"errorMessage\": \"暂停失败，流程未运行\",\n        \"success\": false }\n    }\n    return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":240,"wires":[["39280fec7a243b6d","be9582addda9e660","bf932223988ed392"]]},{"id":"09e9ca0caf4be738","type":"function","z":"b676dbe90b296890","name":"状态请求","func":"if (msg.payload.op == \"status\"){\n    var status = global.get(\"flow_status\")||\"stop\";\n    msg.payload = { \n    \"msg\": \"状态获取成功\", \n    \"code\": 0,\n    \"status\": status , \n    \"errorCode\": \"NO-ERROR\",\n    \"errorMessage\": \"NO-MESSAGE\",\n    \"success\": true\n    }\n    return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":180,"wires":[["39280fec7a243b6d","be9582addda9e660","bf932223988ed392"]]},{"id":"f9d4644239d2cdf6","type":"comment","z":"b676dbe90b296890","name":"1.管理接口","info":"","x":80,"y":240,"wires":[]},{"id":"be9582addda9e660","type":"function","z":"b676dbe90b296890","name":"日志","func":"\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":730,"y":220,"wires":[["221480b30d81fb3b"]]},{"id":"d2fd4d14e3d11584","type":"http in","z":"b676dbe90b296890","name":"TOKEN","url":"/test/token","method":"post","upload":false,"swaggerDoc":"","x":70,"y":360,"wires":[["f32963fe4ffa3ed1"]]},{"id":"f32963fe4ffa3ed1","type":"function","z":"b676dbe90b296890","name":"保存TOKEN","func":"node.warn(msg.payload);\nglobal.set(\"token\",msg.payload.token);\nglobal.set(\"host\",msg.payload.host);\nglobal.set(\"testRecordId\",msg.payload.testRecordId);\nif(msg.payload.testTaskId){\n    global.set(\"testTaskId\",msg.payload.testTaskId);\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":310,"y":480,"wires":[["39280fec7a243b6d","97b8ee4efd068ae6"]]},{"id":"adc5c720c9afd2c8","type":"http request","z":"b676dbe90b296890","name":"上报状态","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"bc9bfb8feab833a5","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":920,"y":540,"wires":[["0355fe86416440f5"]]},{"id":"bf932223988ed392","type":"function","z":"b676dbe90b296890","name":"请求处理","func":"msg.url = `${global.get(\"host\")}/admin/wlp/testTask/modifyStatus`\nmsg.headers = {\n    \"Authorization\":global.get(\"token\"),\n    \"Content-Type\": \"application/json\"\n}\nvar status = global.get(\"flow_status\");\nmsg.payload = {\n     \"testTaskId\": global.get(\"testTaskId\"),\n     \"status\": 1 //状态(0:编辑中,1:未开始,2:进行中,3:已完成,4:已终止,5:已暂停)\n}\nif(\"start\" == status){\n  msg.payload.status = 2;\n}else if(\"pause\" == status ){\n  msg.payload.status = 5;\n}else if (\"stop\" == status) {\n    if(global.get(\"complete\") == true){\n       msg.payload.status = 3;\n    }else{\n       msg.payload.status = 4;\n    }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":740,"y":540,"wires":[["adc5c720c9afd2c8","c4267f9034158918"]]},{"id":"c4267f9034158918","type":"debug","z":"b676dbe90b296890","name":"状态请求","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":980,"y":620,"wires":[]},{"id":"0355fe86416440f5","type":"debug","z":"b676dbe90b296890","name":"请求结果","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1120,"y":540,"wires":[]},{"id":"97b8ee4efd068ae6","type":"file","z":"b676dbe90b296890","name":"写token","filename":"token.json","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"none","x":500,"y":480,"wires":[[]]},{"id":"f14cd04ff0981269","type":"file in","z":"b676dbe90b296890","name":"读token","filename":"token.json","filenameType":"str","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":300,"y":60,"wires":[["704f7527daf47e31"]]},{"id":"704f7527daf47e31","type":"function","z":"b676dbe90b296890","name":"保存TOKEN","func":"node.warn(msg.payload);\nmsg.payload = JSON.parse(msg.payload);\nglobal.set(\"token\",msg.payload.token);\nglobal.set(\"testRecordId\",msg.payload.testRecordId);\n\nglobal.set(\"host\",msg.payload.host);\nif(msg.payload.testTaskId){\n    global.set(\"testTaskId\",msg.payload.testTaskId);\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":60,"wires":[[]]},{"id":"6c9a53e49712d426","type":"function","z":"b676dbe90b296890","name":"开始计时","func":"if (msg.payload.op == \"timing\"){\n    var status = global.get(\"flow_status\");\n    if(status == \"start\"){\n        msg.payload = { \"msg\": \"开始计时成功\", \"code\": -1, \"is_init\": false,\n        \"errorCode\": \"ERROR\",\n        \"errorMessage\": \"启动失败,流程正在运行\",\n        \"success\": true\n        };\n        global.set(\"timing\",new Date().getTime());\n    } else if (status == \"pause\"){\n        msg.payload = { \"msg\": \"开始计时失败，暂停状态\", \"code\": 0, \"is_init\": false ,\n          \"errorCode\": \"NO-ERROR\",\n            \"errorMessage\": \"NO-MESSAGE\",\n            \"success\": false\n        };\n    }else{\n        msg.payload = { \"msg\": \"开始计时失败，停止状态\", \"code\": 0, \"is_init\": true ,\n          \"errorCode\": \"NO-ERROR\",\n            \"errorMessage\": \"NO-MESSAGE\",\n            \"success\": false\n        };\n    }\n    return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":420,"wires":[["39280fec7a243b6d"]]},{"id":"221480b30d81fb3b","type":"subflow:410a1eb0ca263f28","z":"b676dbe90b296890","name":"","x":920,"y":220,"wires":[]},{"id":"8d94373e38b4b93b","type":"function","z":"b676dbe90b296890","name":"删除任务记录","func":"msg.url = `${global.get(\"host\")}/admin/wlp/testRecord/deleteByTaskId`\nmsg.headers = {\n    \"Authorization\":global.get(\"token\"),\n    \"Content-Type\": \"application/json\"\n}\nvar status = global.get(\"flow_status\");\nmsg.payload = {\n     \"testTaskId\": global.get(\"testTaskId\")\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":920,"y":400,"wires":[["f7ef76b37f5cc14c"]]},{"id":"f7ef76b37f5cc14c","type":"http request","z":"b676dbe90b296890","name":"提交删除","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"bc9bfb8feab833a5","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":1120,"y":400,"wires":[["9ac605c53297384b"]]},{"id":"9ac605c53297384b","type":"debug","z":"b676dbe90b296890","name":"删除请求结果","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1300,"y":400,"wires":[]},{"id":"9de1f991e3a02af5","type":"delay","z":"0b96e65cf9d2c6a0","name":"","pauseType":"delayv","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":130,"y":320,"wires":[["776551134e4b332c"]]},{"id":"c8c05d075e4746f9","type":"while-loop","z":"0b96e65cf9d2c6a0","name":"","condi":"!global.get('loop_end')  && global.get('flow_status') !='stop'","limit":false,"limitTime":10000,"time":"wle0a7bbc6f65624de","timeType":"msg","x":290,"y":200,"wires":[["911b4a0523a9a5ca"],["9de1f991e3a02af5"]]},{"id":"007eb3425c45555c","type":"function","z":"0b96e65cf9d2c6a0","name":"测试策略","func":"if(msg.payload.is_init){\n    var loop_mode_input = \"{{loop_mode}}\"//由服务端处理\n    var loop_size_input = \"{{loop_size}}\"//由服务端处理\n    var loop_time_input = \"{{loop_time}}\"//由服务端处理\n    var timeout_input = \"{{timeout}}\"////由服务端处理\n    var device_mode_input = \"{{device_mode}}\"//主动/被动\n\n    var loop_size = 10;//默认值，10次 测试次数\n    var loop_time = 60 * 1000;//默认值，60秒 测试持续时间\n    var timeout = 5000;//默认值，5秒 超时时间\n    var loop_mode = \"size\"; //默认值， size 测试次数 time 持续时间\n    var device_mode = \"active\"//active 主动 passive 被动\n\n    if (!Number.isNaN(Number(loop_size_input))){\n        loop_size = Number(loop_size_input)\n    }\n\n    if (!Number.isNaN(Number(loop_time_input))) {\n        loop_time = Number(loop_time_input)*1000\n    }\n\n    if (!Number.isNaN(Number(timeout_input))) {\n        timeout = Number(timeout_input)\n    }\n\n    var time = loop_time +new Date().getTime();\n\n    if (loop_mode_input == \"time\" || loop_mode_input == \"size\"){\n        loop_mode = loop_mode_input\n    }\n    node.warn(`loop_size:${loop_size},loop_time:${loop_time},timeout:${timeout},loop_mode:${loop_mode},device_mode:${device_mode}`);\n    global.set(\"loop_size\", loop_size);\n    global.set(\"loop_time\",time);\n    global.set(\"loop_mode\",loop_mode);\n    global.set(\"loop_end\",false);\n    global.set(\"result\",[]);//初始化结果\n    global.set(\"has_result\",1);\n    global.set(\"complete\",false);\n    global.set(\"timeout\", timeout);//被测设备超时时间\n    global.set(\"device_mode\",device_mode);\n    global.set(\"active_start_time\", null);\n    \n    if(loop_mode == \"size\"){\n        msg.loop_info = loop_size;\n    }else if(loop_mode == \"time\"){\n        msg.loop_info = loop_time;\n    }\n    msg.delay = 1000 //循环延迟（毫秒）\n    msg.payload = {\"msg\":\"流程初始化成功\",\"code\":0};\n    return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":140,"y":140,"wires":[["c8c05d075e4746f9"]]},{"id":"e6e4c63c39c81a52","type":"function","z":"0b96e65cf9d2c6a0","name":"loop--","func":"//console.error(msg.loop_size);\n global.set(\"timing\",new Date().getTime());\nif(global.get(\"flow_status\")==\"start\"){\n    var loop_mode = global.get(\"loop_mode\");\n    if(loop_mode == \"size\"){\n        var loop_size = global.get(\"loop_size\");\n\n        if(global.get(\"device_mode\") == \"passive\"){//被动设备循环自己计数，主动设备在采集结束后处理\n            loop_size = global.get(\"loop_size\") - 1;\n            global.set(\"loop_size\", loop_size);\n        }\n\n        msg.loop_info = loop_size<0?0:loop_size;\n       \n        if (loop_size<=0){\n            global.set(\"loop_end\",true);\n            global.set(\"active_start_time\", null);\n        }\n    }else if(loop_mode == \"time\"){\n        var current_time = new Date().getTime();\n        var end_time = global.get(\"loop_time\");\n        var diff_time =end_time - current_time;\n        msg.loop_info  = diff_time <= 0 ? 0 : diff_time;;\n        if (diff_time<=0){\n            global.set(\"loop_end\", true);\n            global.set(\"active_start_time\", null);\n        }\n    }\n}\nmsg.payload = { \"msg\": loop_mode == \"time\" ? `剩余测试时间：${msg.loop_info/1000}秒` : `剩余次数：${msg.loop_info}`,\"code\":0};\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":320,"wires":[["c8c05d075e4746f9","c0ad4a617d16fb28","d714d57b5860d3f0"]]},{"id":"c0ad4a617d16fb28","type":"function","z":"0b96e65cf9d2c6a0","name":"是否暂停状态","func":"if(global.get(\"flow_status\")==\"start\"){\n    global.set(\"has_result\",0);\n    global.set(\"start_time\",new Date().getTime());\n    return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":320,"wires":[[]]},{"id":"911b4a0523a9a5ca","type":"function","z":"0b96e65cf9d2c6a0","name":"循环结束","func":"if(global.get(\"flow_status\") != \"stop\"){\n    global.set(\"complete\",true);\n}\nglobal.set(\"flow_status\",\"stop\");\nconst masage= global.get(\"result\").map(item=>item.value)\nmsg.payload = { \"msg\": `测试结束:${masage}`,\"result\":`${ global.get(\"result\")}`,\"length\":`${global.get(\"result\").length}`,\"code\":0};\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":640,"y":200,"wires":[["d714d57b5860d3f0","8886fcac82616356"]]},{"id":"776551134e4b332c","type":"while-loop","z":"0b96e65cf9d2c6a0","name":"","condi":"global.get(\"has_result\")==0 &&!global.get('loop_end')  && global.get('flow_status') !='stop'","limit":false,"limitTime":10000,"time":"wl9cdede6ebf4e3ede","timeType":"msg","x":390,"y":480,"wires":[["e6e4c63c39c81a52"],["aa410320bd84e38e"]]},{"id":"aa410320bd84e38e","type":"function","z":"0b96e65cf9d2c6a0","name":"超时判断","func":"if(global.get(\"flow_status\")==\"start\"){\n\n    //node.error(diff_time);\n\n    var loop_mode = global.get(\"loop_mode\")\n    if(loop_mode == \"time\"){\n            var current_time = new Date().getTime();\n            var end_time = global.get(\"loop_time\");\n            var diff_time =end_time - current_time;\n            msg.loop_info = diff_time;\n            if (diff_time<=0){\n                msg.loop_info = 0\n                global.set(\"loop_end\", true);\n                global.set(\"active_start_time\", null);\n            }\n            msg.payload = {\"msg\":`超时循环信息：${msg.loop_info} `,\"code\":0};\n    }\n  \n\n    var current_time = new Date().getTime();\n    var before_time = global.get(\"start_time\");\n    var diff_time = current_time - before_time;\n    var time_out = global.get(\"timeout\")\n    if (diff_time > time_out){\n        node.error(\"timeout!!\");\n        if(global.get(\"device_mode\") == \"active\"){\n            global.set(\"active_start_time\", null);\n            global.set(\"loop_size\", global.get(\"loop_size\") - 1);\n        }\n        var list = global.get(\"result\") || [];\n        list.push({state:0});\n        global.set(\"has_result\",1);\n        msg.payload = {\"msg\":\"设备连接超时\",\"code\":-1};\n    }\n\n  \n}else{\n    global.set(\"start_time\",new Date().getTime());//暂停状态清除超时时间\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":480,"wires":[["9de1f991e3a02af5","d714d57b5860d3f0"]]},{"id":"d714d57b5860d3f0","type":"function","z":"0b96e65cf9d2c6a0","name":"日志输出","func":"\nreturn msg","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":860,"y":380,"wires":[["c05e9b8dc618272a"]]},{"id":"4546d538e48042f0","type":"http request","z":"0b96e65cf9d2c6a0","name":"上报状态","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"1da57e4df77a3827","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":1020,"y":80,"wires":[["c17f39ca2bc51cf4","12fc73f51765540a"]]},{"id":"7a9ce2b07e67a998","type":"function","z":"0b96e65cf9d2c6a0","name":"请求处理","func":"msg.url = `${global.get(\"host\")}/admin/wlp/testTask/modifyStatus`\nmsg.headers = {\n    \"Authorization\":global.get(\"token\"),\n    \"Content-Type\": \"application/json\"\n}\nvar status = global.get(\"flow_status\");\nmsg.payload = {\n     \"testTaskId\": global.get(\"testTaskId\"),\n     \"status\": 1 //状态(0:编辑中,1:未开始,2:进行中,3:已完成,4:已终止,5:已暂停)\n}\nif(\"start\" == status){\n  msg.payload.status = 2;\n}else if(\"pause\" == status ){\n  msg.payload.status = 5;\n}else if (\"stop\" == status) {\n    if(global.get(\"complete\") == true){\n       msg.payload.status = 3;\n    }else{\n       msg.payload.status = 4;\n    }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":860,"y":80,"wires":[["4546d538e48042f0","e4bd31ca456a7ba4"]]},{"id":"e4bd31ca456a7ba4","type":"debug","z":"0b96e65cf9d2c6a0","name":"状态请求","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1020,"y":140,"wires":[]},{"id":"c17f39ca2bc51cf4","type":"debug","z":"0b96e65cf9d2c6a0","name":"请求结果","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1200,"y":100,"wires":[]},{"id":"22ad132f3d992ef9","type":"subflow:b676dbe90b296890","z":"0b96e65cf9d2c6a0","name":"","x":130,"y":60,"wires":[["3cb129bac3455659"]]},{"id":"c05e9b8dc618272a","type":"subflow:410a1eb0ca263f28","z":"0b96e65cf9d2c6a0","name":"","x":1040,"y":380,"wires":[]},{"id":"186116df9ed1e0f8","type":"comment","z":"0b96e65cf9d2c6a0","name":"主循环","info":"","x":270,"y":240,"wires":[]},{"id":"3e9c93b16d04a9d0","type":"comment","z":"0b96e65cf9d2c6a0","name":"超时循环","info":"","x":320,"y":440,"wires":[]},{"id":"91c262f8a17cb456","type":"http request","z":"0b96e65cf9d2c6a0","name":"上报结果","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"1da57e4df77a3827","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":1040,"y":180,"wires":[["a026af2c6462bfe5","7a9ce2b07e67a998"]]},{"id":"8886fcac82616356","type":"function","z":"0b96e65cf9d2c6a0","name":"请求处理","func":"function convertArrayToCustomJson(inputArray, testTaskId = 1930108505797169152) {\n  // 检查输入是否为数组\n  if (!Array.isArray(inputArray)) {\n    console.error(\"输入必须是一个数组。\");\n    return {\n      data: {\n        records: []\n      },\n      error: \"输入必须是一个数组。\"\n    };\n  }\n\n  const records = inputArray.map(item => {\n    console.error(\"item\", item);\n    return {\n      testTaskId: testTaskId,\n      status:item.state,\n      remark:item.value\n    };\n  });\n\n  return {\n    data: {\n      records: records\n    }\n  };\n}\n\nmsg.url = `${global.get(\"host\")}/admin/wlp/testRecord/add`\nmsg.headers = {\n    \"Authorization\":global.get(\"token\"),\n    \"Content-Type\": \"application/json\"\n}\nmsg.payload = convertArrayToCustomJson(global.get(\"result\"),global.get(\"testTaskId\"))\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":200,"wires":[["91c262f8a17cb456","98cddc5c9129fed1"]]},{"id":"98cddc5c9129fed1","type":"debug","z":"0b96e65cf9d2c6a0","name":"状态请求","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1040,"y":240,"wires":[]},{"id":"a026af2c6462bfe5","type":"debug","z":"0b96e65cf9d2c6a0","name":"请求结果","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1180,"y":180,"wires":[]},{"id":"12fc73f51765540a","type":"function","z":"0b96e65cf9d2c6a0","name":"请求处理","func":"msg.url = `${global.get(\"host\")}/admin/wlp/testTask/generateReport`\nmsg.headers = {\n    \"Authorization\":global.get(\"token\"),\n    \"Content-Type\": \"application/json\"\n}\nconst status=msg.payload.status\nmsg.payload = {\n     \"testTaskId\": global.get(\"testTaskId\"),\n      \"testRecordId\":global.get(\"testRecordId\"),\n}\nif(global.get(\"complete\")){\n    return msg;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1200,"y":40,"wires":[["9ee7fa705a13a85a"]]},{"id":"9ee7fa705a13a85a","type":"http request","z":"0b96e65cf9d2c6a0","name":"生成报告","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"1da57e4df77a3827","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":1360,"y":40,"wires":[["b179287f05693805"]]},{"id":"b179287f05693805","type":"debug","z":"0b96e65cf9d2c6a0","name":"请求结果","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1520,"y":40,"wires":[]},{"id":"3cb129bac3455659","type":"delay","z":"0b96e65cf9d2c6a0","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":340,"y":60,"wires":[["007eb3425c45555c"]]},{"id":"df4aa3974a4b28c2","type":"subflow:da7a0f42ae981076","z":"f2b3c541197aea23","name":"","x":480,"y":220,"wires":[["93ec4400cac2352e"]]},{"id":"c24cc8c42d8363c8","type":"subflow:0b96e65cf9d2c6a0","z":"f2b3c541197aea23","name":"","x":120,"y":220,"wires":[["0f50416f453e37e3"],[]]},{"id":"0f50416f453e37e3","type":"function","z":"f2b3c541197aea23","name":"采集开始","func":"if(global.get('flow_status') !='stop'){\n    global.set(\"lastTime\",0)\n    global.set(\"throttleTime\", 1000 )// 节流间隔，单位毫秒\n    var current_time = new Date().getTime();\n    msg.collect_start_time = current_time;\n    return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":220,"wires":[["df4aa3974a4b28c2"]]},{"id":"93ec4400cac2352e","type":"function","z":"f2b3c541197aea23","name":"采集结束","func":"function check_loop_mode(){\n    if (global.get(\"device_mode\") != \"passive\"){\n        var loop_mode = global.get(\"loop_mode\");\n        if (loop_mode == \"size\" && global.get(\"loop_size\") > 0) {\n            node.error(`loop_size:${global.get(\"loop_size\")},time:${time}`)\n            var loop_size = global.get(\"loop_size\");\n            loop_size = global.get(\"loop_size\") - 1;\n            global.set(\"loop_size\", loop_size);\n            if (loop_size == 0) {\n                global.set(\"loop_end\", true);\n            }\n        }\n    }\n}\n\nif(global.get('flow_status') !='stop'){\n    var type_input = \"{{type_input}}\"//由服务端处理\n    if(type_input == \"send_speed\"){\n        if(global.get(\"device_mode\") == \"passive\"){//被动设备\n            var end_time = new Date().getTime();\n            var start_time = msg.collect_start_time;\n            var time = end_time - start_time\n            var speed = parseFloat((1000 / time).toFixed(2))\n            msg.speed = speed\n            node.warn(`采集耗时：${time}，发送速率：${speed}Hz`)\n            return msg;\n        }else{//主动设备\n            if (!isNaN(msg.collect_start_time)){//兼容被动设备\n                global.set(\"active_start_time\", msg.collect_start_time);\n            }\n            node.warn(`global.get(\"loop_end\"):${global.get(\"loop_end\")}`);\n            if(global.get(\"loop_end\") == false){\n                var active_time = global.get(\"active_start_time\", null);\n                node.warn(`active_time:${active_time}}`);\n                if (active_time != null) {\n                    var end_time = new Date().getTime();\n                    var start_time = active_time;\n                    var time = end_time - start_time\n                    var speed = parseFloat((1000 / time).toFixed(2))\n                    msg.speed = speed\n                    node.warn(`采集耗时：${time}，发送速率：${speed}Hz`)\n                    //node.warn(`set active_start_time1:${end_time}`)\n                    global.set(\"active_start_time\", end_time);\n                    check_loop_mode();\n                    return msg;\n                } else {\n                    var current_time = new Date().getTime();\n                    global.set(\"active_start_time\", current_time );\n                }\n            }\n        }\n    }else{\n        check_loop_mode();\n        return msg;\n    }\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":220,"wires":[["c0263338b3e8dd5a"]]},{"id":"c0263338b3e8dd5a","type":"subflow:c6f6c91c824708d6","z":"f2b3c541197aea23","name":"","x":840,"y":220,"wires":[]}]